package model

import (
	j_log "config-client-v1/init/j-log"
	"config-client-v1/test/serial-server/pkg"
	"config-client-v1/utils/tools"
	"encoding/json"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"time"
)

//  通过变量地址获取变量信息

func GetVariableByAddr(projectID int, addr string) (v *Variable, err error) {
	v = &Variable{}
	if addr != "" {
		err = DB.Where("project_id = ? AND addr = ?", projectID, addr).First(v).Error
	}
	return
}

// CheckAddrSpecType 通过变量地址判断变量类型是否匹配
func CheckAddrSpecType(projectID int, addr string, specType string) (equal bool) {
	if addr == "" {
		return false
	}
	v, err := GetVariableByAddr(projectID, addr)
	if err != nil {
		return false
	}
	if v.SpecType != specType {
		return false
	}
	return true
}

// CreateFrameRawData 生成发送帧原始数据, 适用于RS485,RS232,TCP, PLC(需做调整)
func CreateFrameRawData(list []*FrameItemParam) (data []byte, err error) {
	data = make([]byte, 0)
	uniqueKeyBoolMap := make(map[string]bool)
	for i := 0; i < len(list); i++ {
		row := list[i]
		l := row.End - row.Start + 1
		if row.InputMethod == 0 {
			if row.RawData == "" {
				switch row.CheckMethod {
				case "sum":
					if l != 1 {
						err = errors.New("sum校验和只能占用一个字节")
						return
					}
					byteSum := pkg.GetBytesSum(data, row.CheckStart, row.CheckEnd)
					data = append(data, byteSum)
				case "crc16":
					if l != 2 {
						err = errors.New("crc16校验只能占用两个字节")
						return
					}
					result := pkg.GetCrc16Result(data[row.CheckStart : row.CheckEnd+1])
					data = append(data, result...)
				default:
					err = errors.New("校验方法参数无效")
					return
				}
			} else {
				b, _ := pkg.HexStrToByteArray(row.RawData)
				data = append(data, b...)
			}
			continue
		}

		v, ok := VariablesMap.Get(row.Addr)
		switch row.SpecType {
		case "uint8":
			fallthrough
		case "int8":
			if !ok {
				j_log.LogError([]interface{}{fmt.Sprintf("WARNING 未找到发送帧变量%s ,默认使用零值发送", row.Addr)})
				data = append(data, 0x00)
			} else {
				tmpB := byte(v.Value)
				data = append(data, tmpB)
			}
		case "uint16":
			fallthrough
		case "int16":
			if !ok {
				j_log.LogError([]interface{}{fmt.Sprintf("WARNING 未找到发送帧变量%s ,默认使用零值发送", row.Addr)})
				data = append(data, 0x00, 0x00)
			} else {
				tmpV := uint16(v.Value)
				tmpB := tools.Uint16ToBytes(tmpV, row.Endian)
				realBytes := tools.BytesRemoveLeft(tmpB[:], l)
				data = append(data, realBytes...)
			}
		case "uint32":
			fallthrough
		case "int32":
			if !ok {
				j_log.LogError([]interface{}{fmt.Sprintf("WARNING 未找到发送帧变量%s ,默认使用零值发送", row.Addr)})
				data = append(data, 0x00, 0x00, 0x00, 0x00)
			} else {
				tmpV := uint32(v.Value)
				tmpB := tools.Uint32ToBytes(tmpV, row.Endian)
				realBytes := tools.BytesRemoveLeft(tmpB[:], l)
				data = append(data, realBytes...)
			}
		case "uint64":
			fallthrough
		case "int64":
			if !ok {
				j_log.LogError([]interface{}{fmt.Sprintf("WARNING 未找到发送帧变量%s ,默认使用零值发送", row.Addr)})
				data = append(data, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
			} else {
				tmpV := uint64(v.Value)
				tmpB := tools.Uint64ToBytes(tmpV, row.Endian)
				realBytes := tools.BytesRemoveLeft(tmpB[:], l)
				data = append(data, realBytes...)
			}
		case "float32":
			if !ok {
				j_log.LogError([]interface{}{fmt.Sprintf("WARNING 未找到发送帧变量%s ,默认使用零值发送", row.Addr)})
				data = append(data, 0x00, 0x00, 0x00, 0x00)
			} else {
				tmpV := float32(v.Value)
				tmpB := tools.Float32ToBytes4(tmpV, row.Endian)
				data = append(data, tmpB[:]...)
			}
		case "float64":
			if !ok {
				j_log.LogError([]interface{}{fmt.Sprintf("WARNING 未找到发送帧变量%s ,默认使用零值发送", row.Addr)})
				data = append(data, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
			} else {
				tmpV := v.Value
				tmpB := tools.Float64ToBytes(tmpV, row.Endian)
				data = append(data, tmpB[:]...)
			}
		case "bool":
			if row.MultiByte == 1 {
				// 多字节布尔值
				if row.BoolBits > 7 {
					err = errors.New(fmt.Sprintf("%s bool_bits 参数异常", row.FieldName))
					return
				}
				if !uniqueKeyBoolMap[fmt.Sprintf("%d", row.Start)] {
					uniqueKeyBoolMap[fmt.Sprintf("%d", row.Start)] = true
					if !ok {
						j_log.LogError([]interface{}{fmt.Sprintf("WARNING 未找到发送帧变量%s ,默认使用零值发送", row.Addr)})
						data = append(data, 0x00)
					} else {
						tmpB := byte(v.Value) // TODO 很复杂
						data = append(data, tmpB)
					}
				}
			} else {
				// 单字节布尔值
				if !ok {
					j_log.LogError([]interface{}{fmt.Sprintf("WARNING 未找到发送帧变量%s ,默认使用零值发送", row.Addr)})
					data = append(data, 0x00)
				} else {
					tmpB := byte(v.Value)
					data = append(data, tmpB)
				}
			}
		default:
			err = errors.New(fmt.Sprintf("%s 无效的数据类型", row.FieldName))
			return
		}
		continue
	}
	return
}

func CreateFrameCoilsRawData(list []*FrameItemParam) (data []bool, err error) {
	data = make([]bool, 0)
	for i := 0; i < len(list); i++ {
		row := list[i]
		v, ok := VariablesMap.Get(row.Addr)
		if !ok {
			j_log.LogError([]interface{}{fmt.Sprintf("WARNING 未找到布尔变量%s ,默认使用False发送", row.Addr)})
			data = append(data, false)
		} else {
			if v.Value == 0.0 {
				data = append(data, false)
			} else {
				data = append(data, true)
			}
		}
	}
	return
}

// RecoveryProjectRunningState 恢复工程运行状态 0.未运行 1.正在启动 2.已启动 3.正在停止 4.已停止 5.正在重连
// 上次程序停止时状态为 1,2,5 的设备,在程序启动时恢复运行状态, 其他的状态重置为4
func RecoveryProjectRunningState() (err error) {
	stateArr := []int{1, 2, 5}
	stateMap := make(map[int]bool)
	stateMap[1] = true
	stateMap[2] = true
	stateMap[5] = true
	DB.Model(&FSEquip{}).Where("running_state NOT IN ?", stateArr).Updates(map[string]interface{}{
		"running_state": 4,
	})
	DB.Model(&PPE{}).Where("running_state NOT IN ?", stateArr).Updates(map[string]interface{}{
		"running_state": 4,
	})
	fseList := make([]*FSEquip, 0)
	ppeList := make([]*PPE, 0)
	err = DB.Where("running_state IN ?", stateArr).Find(&fseList).Error
	if err != nil {
		return
	}
	err = DB.Where("running_state IN ?", stateArr).Find(&ppeList).Error
	if err != nil {
		return
	}
	// 获取上次程序停止时运行的工程ID
	projectID := 0
	if len(fseList) > 0 {
		projectID = fseList[0].ProjectID
	} else if len(ppeList) > 0 {
		projectID = ppeList[0].ProjectID
	} else {
		//err = errors.New("未找到待恢复运行的工程")
		return
	}
	project := Project{}
	err = DB.
		Preload("PPEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FrameList", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			}).Where("running_state IN ?", stateArr)
		}).
		Preload("FSEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FSModule", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			}).Where("running_state IN ?", stateArr)
		}).
		Where("id = ? AND del = 0", projectID).Limit(1).Find(&project).Error

	if err != nil {
		return
	}
	if project.ID == 0 {
		err = errors.New("工程ID异常")
		return
	}

	if RunningProject.ID != 0 {
		if RunningProject.ID == projectID {
			err = errors.New("当前工程正在运行中")
			return
		}
		err = errors.New("其他工程正在运行中")
		return
	}
	err = ProjectStartHook(projectID)
	if err != nil {
		return
	}

	for _, ppe := range project.PPEList {
		go func(ppe *PPE) {
			for _, frame := range ppe.FrameList {
				frame.JsonDetail, _ = GetLatestFrameJsonDetail(frame)
			}
			err = ppe.Start()
			if err != nil {
				j_log.LogError([]interface{}{fmt.Sprintf("第三方设备【%s】启动失败 %s", ppe.Name, err.Error())})
				return
			}
		}(ppe)
	}

	for _, fse := range project.FSEList {
		go func(fse *FSEquip) {
			fseExist, ok := RunningFSMap.Get(fse.ID)
			if ok && fseExist.RunningState != 4 {
				j_log.LogError([]interface{}{fmt.Sprintf("自研设备【%s】运行中, 不可重复启动", fse.Name)})
				return
			}

			fse.CalculateDataScale()
			RunningFSMap.Set(fse.ID, fse)
			fse.RunningState = 1 // 正在启动
			_ = fse.UpdateRunningState(1, "正在启动")
			go fse.RunFSTcpClient()
			startTime := time.Now()
			for {
				time.Sleep(100 * time.Millisecond)
				if fse.RunningState == 2 {
					break
				}
				if time.Since(startTime) > time.Second*5 {
					j_log.LogError([]interface{}{fmt.Sprintf("自研设备【%s】启动超时", fse.Name)})
					_ = SysAlertSave(&SysAlert{
						ProjectID:  fse.ProjectID,
						Const:      "ALERT_FS",
						Alert:      "自研设备启动超时",
						RelationID: fse.ID,
					})
					return
				}
			}
			if fse.Stage == 1 {
				go func(fse *FSEquip) {
					time.Sleep(1 * time.Second)
					data := GenFSFrameData0601()
					_, err = fse.Write(data)
					if err != nil {
						j_log.LogError([]interface{}{"首次握手tcp发送失败", fse.Name, fse.IP, fse.Port, err.Error()})
						return
					}
					j_log.LogInfo([]interface{}{"首次握手tcp发送成功", fse.Name, fse.IP, fse.Port})
				}(fse)
			}
		}(fse)
	}
	return
}

// GetLatestFrameJsonDetail 更新帧变量属性, 保持与最新的变量一致
func GetLatestFrameJsonDetail(frame *Frame) (jsonDetail string, err error) {
	err = json.Unmarshal([]byte(frame.JsonDetail), &frame.FrameItemParam)
	if err != nil {
		return
	}
	addrs := make([]string, 0)
	for _, row := range frame.FrameItemParam {
		if row.Addr != "" {
			addrs = append(addrs, row.Addr)
		}
	}
	if len(addrs) > 0 {
		latestList := make([]*FrameItem, 0)
		err = DB.Where("project_id = ? AND addr in ?", frame.ProjectID, addrs).
			Select("*").Find(&latestList).Error
		if err != nil {
			return
		}
		if len(latestList) > 0 {
			for _, row := range frame.FrameItemParam {
				if row.InputMethod == 1 {
					for _, row2 := range latestList {
						if row.Addr == row2.Addr {
							row.FieldName = row2.FieldName
							row.SpecType = row2.SpecType
							row.RealSpecType = row2.RealSpecType
							row.Endian = row2.Endian
							row.RatioOperator = row2.RatioOperator
							row.Ratio = row2.Ratio
							row.Fe = row2.Fe
							row.Precision = row2.Precision
							row.RawData = row2.FieldName
						}
					}
				}
			}
		}
	}
	jsonB, _ := json.Marshal(frame.FrameItemParam)
	jsonDetail = string(jsonB)
	return
}
